package com.clp.protocol.iec104.server;

import com.clp.protocol.iec104.server.async.SlaveFuture;
import com.clp.protocol.iec104.server.async.SlaveFutureListener;
import com.clp.protocol.core.server.NettyServer;

import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;

public class Iec104SlaveManager implements Closeable {
    private static volatile Iec104SlaveManager slaveManager;

    public static Iec104SlaveManager get() {
        // 懒加载
        if (slaveManager != null && !slaveManager.isClosed()) {
            return slaveManager;
        }
        synchronized (Iec104SlaveManager.class) {
            if (slaveManager != null && !slaveManager.isClosed()) {
                return slaveManager;
            }
            slaveManager = new Iec104SlaveManager();
        }
        return slaveManager;
    }

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            synchronized (Iec104SlaveManager.class) {
                if (slaveManager == null || slaveManager.isClosed()) return;
                try {
                    slaveManager.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }));
    }

    private final NettyServer nettyServer = new NettyServer(); // 内嵌服务端
    private final SlaveContainer container; // 子站容器

    private Iec104SlaveManager() {
        this.container = new SlaveContainer(nettyServer.scheduledExecutorService());
    }

    public synchronized boolean isClosed() {
        return nettyServer.isClosed();
    }

    public synchronized SlaveFuture<Void> openSlave(SlaveConfig cfg) throws Throwable {
        checkNotClosed();

        // 检查配置
        cfg.check();
        // 检查 从站ip+端口号是否已经打开
        String localHost = cfg.getLocalHost();
        int localPort = cfg.getLocalPort();
        if (hasSlave(localHost, localPort)) {
            throw new PortAlreadyBindException(localHost, localPort);
        }

        InSlave slave = createSlave(cfg);
        try {
            SlaveFuture<Void> openFuture = slave.open();
            openFuture.addListener(new SlaveFutureListener<Void>() {
                @Override
                public void operationComplete(SlaveFuture<Void> future) {
                    if (future.isSuccess()) {
                        container.add(future.slave());
                    }
                }
            });
            return openFuture;
        } catch (Throwable ex) {
            return slave.newPromise(Void.class).setFailure(ex.getCause());
        }
    }

    private void checkNotClosed() {
        if (isClosed()) {
            throw new RuntimeException("客户端已关闭！");
        }
    }

    public boolean hasSlave(String localHost, int localPort) {
        return container.contains(localHost, localPort);
    }

    @Nullable
    public Slave getSlave(String localHost, int localPort) {
        return container.getOne(localHost, localPort);
    }

    private InSlave createSlave(SlaveConfig cfg) {
        InSlave slave = new InSlave(nettyServer, cfg);
        return slave;
    }

    public synchronized SlaveFuture<Void> closeSlave(Slave slave) {
        checkNotClosed();
        try {
            SlaveFuture<Void> closeFuture = ((InSlave) slave).close();
            closeFuture.addListener(new SlaveFutureListener<Void>() {
                @Override
                public void operationComplete(SlaveFuture<Void> future) {
                    if (future.isSuccess()) {
                        container.remove(future.slave());
                    }
                }
            });
            return closeFuture;
        } catch (Throwable ex) {
            return slave.newPromise(Void.class).setFailure(ex);
        }
    }

    public synchronized CompletableFuture<Boolean> closeAllSlaves() {
        throw new UnsupportedOperationException();
    }

    @Override
    public synchronized void close() throws IOException {
        if (isClosed()) return;
        closeAllSlaves();
    }
}
